﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WarningForeverClone.src.weapons;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace WarningForeverClone.src.ships
{
    public class ShipPart
    {
        ShipPart parent;
        protected List<ShipPart> parts;

        Vector2 offset; //(x,y) offset from ship top left
        Texture2D texture;
        string texturePath;
        bool inFront;

        float maxHealth;
        private float health;

        int shieldType;

        protected static Dictionary<String, Color[]> textureBits = new Dictionary<string, Color[]>();
        static Texture2D blueShield, redShield, greenShield;
        int type;

        public ShipPart(int type, ShipPart parent, string texturePath, Vector2 offset, bool inFront, float maxHealth)
        {
            this.parent = parent;
            this.texturePath = texturePath;
            this.offset = offset;
            this.inFront = inFront;
            this.maxHealth = maxHealth;
            this.health = maxHealth;
            this.type = type;
            

            parts = new List<ShipPart>();

            if (blueShield == null)
            {
                if (MainGame.ContentManager != null)
                {
                    greenShield = MainGame.ContentManager.Load<Texture2D>("Textures/Boss/greenShield");
                    blueShield = MainGame.ContentManager.Load<Texture2D>("Textures/Boss/blueShield");
                    redShield = MainGame.ContentManager.Load<Texture2D>("Textures/Boss/redShield");
                }
            }
        }

        public virtual void LoadContent(ContentManager contentManager)
        {
            texture = contentManager.Load<Texture2D>(texturePath);
            if(!textureBits.ContainsKey(texturePath))
            {
                Color[] bits = new Color[texture.Width * texture.Height];
                texture.GetData<Color>(bits);
                textureBits.Add(texturePath, bits);
            }


            foreach (ShipPart child in parts)
            {
                child.LoadContent(contentManager);
            }
        }

        public void Draw(SpriteBatch sb)
        {
            if (!Alive)
                return;

            if (inFront)
            {
                foreach (ShipPart child in parts)
                {
                    child.Draw(sb);
                }
                sb.Draw(texture, GetScreenCoordinates(), Color.White);
            }
            else
            {
                sb.Draw(texture, GetScreenCoordinates(), Color.White);
                foreach (ShipPart child in parts)
                {
                    child.Draw(sb);
                }
            }

            if (this is Boss)
            {
                if (shieldType == 0)
                    sb.Draw(Boss.greenHullShield, GetScreenCoordinates(), Color.White);
                else if (shieldType == 1)
                    sb.Draw(Boss.blueHullShield, GetScreenCoordinates(), Color.White);
                else if (shieldType == 2)
                    sb.Draw(Boss.redHullShield, GetScreenCoordinates(), Color.White);
            }
            else
            {
                if(shieldType == 0)
                    sb.Draw(greenShield, GetScreenCoordinates() + new Vector2(10, 0), Color.White);
                else if (shieldType == 1)
                    sb.Draw(blueShield, GetScreenCoordinates() + new Vector2(10, 0), Color.White);
                else if (shieldType == 2)
                    sb.Draw(redShield, GetScreenCoordinates() + new Vector2(10, 0), Color.White);
            }


        }

        public void CheckCollisions(List<Weapon> weapons)
        {
            if (!Alive)
                return;

            if (inFront)
            {
                CheckMyCollisions(weapons);
                CheckChildCollisions(weapons);
            }
            else
            {
                CheckChildCollisions(weapons);
                CheckMyCollisions(weapons);
            }
        }
        protected virtual void CheckMyCollisions(List<Weapon> weapons)
        {
            for (int x = 0; x < weapons.Count; x++)
            {
                Weapon w = weapons[x];

                if (NonRotatedPerPixelCollision(w, GetScreenCoordinates(), new Vector2(texture.Width, texture.Height), textureBits[texturePath]))
                {
                    if (type == 0) //part is green laser cannon
                    {
                        if (w is GreenLaser) //weapon is green laser
                        {
                             MainGame.particleManager.CreateMiss(w.Position);
                        }
                        else
                        {
                            if (shieldType == 1 && w is BlueLaser || shieldType == 2 && w is RedLaser)
                            {
                                MainGame.particleManager.CreateShieldHit(w.Position);
                                health -= w.Damage / 4;
                            }
                            else
                            {
                                MainGame.particleManager.CreateHit(w.Position);
                                health -= w.Damage;
                            }
                        } 
                    }
                    else if (type == 1) //part is blue laser cannon
                    {
                        if (w is BlueLaser) //weapon is blue laser
                        {
                            MainGame.particleManager.CreateMiss(w.Position);
                        }
                        else
                        {
                            if (shieldType == 0 && w is GreenLaser || shieldType == 2 && w is RedLaser)
                            {
                                MainGame.particleManager.CreateShieldHit(w.Position);
                                health -= w.Damage / 4;
                            }
                            else
                            {
                                MainGame.particleManager.CreateHit(w.Position);
                                health -= w.Damage;
                            }
                        } 
                    }
                    else if (type == 2) //part is red laser cannon
                    {
                        if (w is RedLaser)
                        {
                            MainGame.particleManager.CreateMiss(w.Position);
                        }
                        else
                        {
                            if (shieldType == 0 && w is GreenLaser || shieldType == 1 && w is BlueLaser)
                            {
                                MainGame.particleManager.CreateShieldHit(w.Position);
                                health -= w.Damage / 4;
                            }
                            else
                            {
                                MainGame.particleManager.CreateHit(w.Position);
                                health -= w.Damage;
                            }
                        }
                    }
                    else if (type == 4) //part is rocket cannon
                    {
                        if (shieldType == 0 && w is GreenLaser || shieldType == 1 && w is BlueLaser || shieldType == 2 && w is RedLaser)
                        {
                            MainGame.particleManager.CreateShieldHit(w.Position);
                            health -= w.Damage / 4;
                        }
                        else
                        {
                            MainGame.particleManager.CreateHit(w.Position);
                            health -= w.Damage;
                        }
                    }

                    w.Collide(this);
                }
            }
        }
        private void CheckChildCollisions(List<Weapon> weapons)
        {
            foreach (ShipPart child in parts)
            {
                child.CheckCollisions(weapons);
            }
        }


        /// <summary>
        /// Get the position of this part in the screen space
        /// </summary>
        /// <returns></returns>
        protected Vector2 GetScreenCoordinates()
        {
            Vector2 totalOffset = new Vector2();

            //recurse up to the hull of the ship
            ShipPart current = this;
            while (current.parent != null)
            {
                totalOffset += current.offset;
                current = current.parent;
            }

            //add all offsets to the center of the boss
            Boss boss = (Boss)current;
            return boss.Position + totalOffset;
        }

        /// <summary>
        /// Checks for a rectangle-rectangle collision
        /// </summary>
        /// <param name="x">Position of rectangle 1</param>
        /// <param name="dx">Dimensions of renctangle 1</param>
        /// <param name="y">Position of rectangle 2</param>
        /// <param name="dy">Dimensions of rectnagle 2</param>
        /// <returns></returns>
        protected static bool RectRectCollision(Vector2 x, Vector2 dx, Vector2 y, Vector2 dy)
        {

            float top = Math.Max(x.Y, y.Y);
            float bottom = Math.Min(x.Y + dx.Y, y.Y + dy.Y);
            if (top < bottom)
            {
                float left = Math.Max(x.X, y.X);
                float right = Math.Min(x.X + dx.X, y.X + dy.X);
                if (left < right)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Check if a weapon has collided with the boss
        /// </summary>
        /// <param name="weapon"></param>
        /// <param name="boss"></param>
        /// <returns></returns>
        public static bool NonRotatedPerPixelCollision(Weapon weapon, Vector2 pos, Vector2 dimensions, Color[] bits)
        {
            Vector2 partPosition = pos;
            if (!RectRectCollision(partPosition, new Vector2(dimensions.X, dimensions.Y),
                                    weapon.Position, new Vector2(weapon.Texture.Width, weapon.Texture.Height)))
                return false;

            // Check every point within the intersection bounds
            int bossX = (int)partPosition.X;
            int bossY = (int)partPosition.Y;

            int weaponX = (int)weapon.Position.X;
            int weaponY = (int)weapon.Position.Y;

            int top = (int)Math.Max(partPosition.Y, weapon.Position.Y);
            int bottom = (int)Math.Min(partPosition.Y + dimensions.Y, weapon.Position.Y + weapon.Texture.Height);


            int left = (int)Math.Max(partPosition.X, weapon.Position.X);
            int right = (int)Math.Min(partPosition.X + dimensions.X, weapon.Position.X + weapon.Texture.Width);

            Color[] weaponTexture = weapon.TextureData;
            Color[] shipTexture = bits;

            for (int y = (int)top; y < bottom; y += 10)
            {
                for (int x = (int)left + 1; x < right; x += 20)
                {
                    if (shipTexture[(x - bossX) + (y - bossY) * (int)dimensions.X].A > 50 &&
                        weaponTexture[(x - weaponX) + (y - weaponY) * weapon.Texture.Width].A > 50)
                    {
                        return true;
                    }

                }
            }

            return false;
        }

        /// Determines if there is overlap of the non-transparent pixels between two
        /// sprites.
        /// 
        /// World transform of the first sprite.
        /// Width of the first sprite's texture.
        /// Height of the first sprite's texture.
        /// Pixel color data of the first sprite.
        /// World transform of the second sprite.
        /// Width of the second sprite's texture.
        /// Height of the second sprite's texture.
        /// Pixel color data of the second sprite.
        /// True if non-transparent pixels overlap; false otherwise
        public static bool RotatedPerPixelCollision(
            Matrix transformA, int widthA, int heightA, Color[] dataA,
            Matrix transformB, int widthB, int heightB, Color[] dataB)
        {

            //http://www.virtualrealm.com.au/mykre/XNA-Rotated-PerPixel-Collision

            // Calculate a matrix which transforms from A's local space into
            // world space and then into B's local space
            Matrix transformAToB = transformA * Matrix.Invert(transformB);
            // When a point moves in A's local space, it moves in B's local space with a
            // fixed direction and distance proportional to the movement in A.
            // This algorithm steps through A one pixel at a time along A's X and Y axes
            // Calculate the analogous steps in B:
            Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
            Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);
            // Calculate the top left corner of A in B's local space
            // This variable will be reused to keep track of the start of each row
            Vector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);
            // For each row of pixels in A
            for (int yA = 0; yA < heightA; yA++)
            {
                // Start at the beginning of the row
                Vector2 posInB = yPosInB;
                // For each pixel in this row
                for (int xA = 0; xA < widthA; xA++)
                {
                    // Round to the nearest pixel
                    int xB = (int)Math.Round(posInB.X);
                    int yB = (int)Math.Round(posInB.Y);
                    // If the pixel lies within the bounds of B
                    if (0 <= xB && xB < widthB &&
                        0 <= yB && yB < heightB)
                    {

                        // Get the colors of the overlapping pixels
                        Color colorA = dataA[xA + yA * widthA];
                        Color colorB = dataB[xB + yB * widthB];
                        // If both pixels are not completely transparent,
                        if (colorA.A > 50 && colorB.A > 50)
                        {
                            // then an intersection has been found
                            return true;
                        }
                    }
                    // Move to the next pixel in the row
                    posInB += stepX;
                }
                // Move to the next row
                yPosInB += stepY;
            }
            // No intersection found
            return false;
        }

        public bool Alive
        {
            get { return health > 0; }
        }

        public Texture2D Texture
        {
            get { return texture; }
        }
        public Vector2 Offset
        {
            get { return offset; }
        }
        public int Width
        {
            get { return texture.Width; }
        }
        public int Height
        {
            get { return texture.Height; }
        }
        public int ShieldType
        {
            get { return shieldType; }
            set { shieldType = value; }
        }

        public float Health
        {
            get { return health; }
            set { health = value; }
        }
    }
}
